/**
 * @ClassName ResolutionUtil
 * @Description 分辨率计算工具
 * @Author xuyizhuo
 * @Date 2023/11/25 16:40
 */
export default class ResolutionUtil {
    // 相机视角计算参数
    static METERS_PER_UNIT = {
        // use the radius of the Normal sphere
        radians: 6370997 / (2 * Math.PI),
        degrees: (2 * Math.PI * 6370997) / 360,
        ft: 0.3048,
        m: 1,
        'us-ft': 1200 / 3937,
    }

    /**
     * 地图分辨率计算三维相机距离
     * @param {number} distance 相机距离
     * @param {number} latitude 中心点的纬度
     * @param scene
     * @returns {number}
     */
    static calcResolutionForDistance(distance: number, latitude: number, scene: any) {
        const canvas = scene.canvas
        const camera = scene.camera
        const fovy = camera.frustum.fovy
        const metersPerUnit = this.METERS_PER_UNIT.m
        const visibleMeters = 2 * distance * Math.tan(fovy / 2)
        const relativeCircumference = Math.cos(Math.abs(latitude))
        const visibleMapUnits = visibleMeters / metersPerUnit / relativeCircumference
        return visibleMapUnits / canvas.clientHeight
    }

    /**
     * 通过地图层级（zoom）计算地图分辨率
     * @param {number} resolution 分辨率
     * @returns {number}
     */
    static getZoomForResolution(resolution: number) {
        const defaultTileSize = 256
        const zoomFactor = 2
        const minZoom = 0
        const size = (360 * this.METERS_PER_UNIT.degrees) / this.METERS_PER_UNIT.m
        const defaultMaxResolution = size / defaultTileSize / zoomFactor ** minZoom
        const maxResolution = defaultMaxResolution / zoomFactor ** minZoom
        return Math.log(maxResolution / resolution) / Math.log(zoomFactor) - 1
    }

    /**
     * 通过地图层级zoom计算分辨率
     * @param {number} zoom 地图层级
     * @returns
     */
    static getResolutionForZoom(zoom: number) {
        const defaultTileSize = 256
        const zoomFactor = 2
        const metersPreUnit = 1 // 默认墨卡托投影，为1，如果地图设置其他投影，这个值会变。当然 metersPreUnit 是一个很大的简化，它没有考虑椭球体/地形，这也导致此算法并不是特别准确。
        const size = (2 * Math.PI * 6370997) / metersPreUnit
        const maxResolution = size / defaultTileSize
        // 计算分辨率
        // const resolution = Math.pow(Math.E, Math.log(maxResolution) - (zoom + 1 + 0.5) * Math.log(zoomFactor));
        return Math.E ** (Math.log(maxResolution) - (zoom + 1) * Math.log(zoomFactor))
    }

    /**
     * 将度数转换为弧度
     *
     * @param {number} angleInDegrees 角度，以度为单位
     * @return {number} 弧度角度
     */
    static toRadians(angleInDegrees: number) {
        return (angleInDegrees * Math.PI) / 180
    }

    /**
     * 通过分辨率计算相机高度，参考ol-cesium计算方式
     * @param {*} resolution 地图分辨率
     * @param {*} latitude 弧度制纬度
     * @param {*} scene 三维场景，需要场景的容器可见，不然算出来是错误的
     * @param {*} metersPerUnit 地图单位米数量，Openlayers的MapView的Projection中getMetersPerUnit()的取值，不同坐标系结果不同
     * @returns
     */
    static calcDistanceForResolution(resolution: number, latitude: number, scene: any, metersPerUnit: number) {
        const camera = scene.camera
        // @ts-ignore TS2341
        const fovy = camera.frustum.fovy // 垂直视场vertical field of view
        // console.log("fovy", fovy);

        // 2D中可见的“地图单元”数量（垂直）
        const visibleMapUnits =
            resolution * scene.canvas.parentElement.parentElement.parentElement.parentElement.parentElement.clientHeight
        // console.log("visibleMapUnits", visibleMapUnits);

        // metersPerUnit没有考虑纬度，但它应该随着纬度的增加而降低——我们必须进行补偿。
        // 在3D中，不可能将分辨率保持在一个以上的点，因此仅使用“目标”点的纬度才有意义。
        const relativeCircumference = Math.cos(Math.abs(latitude))

        // 3D中应该有多少米可见
        const visibleMeters = visibleMapUnits * metersPerUnit * relativeCircumference

        // 查看计算长度所需的距离（以米为单位）distance required to view the calculated length in meters
        //
        //  fovy/2
        //    |\
        //  x | \
        //    |--\
        // visibleMeters/2
        // 注意: 这个计算不是绝对精确的，因为 metersPerUnit 是一个很大的简化。它没有考虑椭球/地形.
        return visibleMeters / 2 / Math.tan(fovy / 2)
    }
}
